#include "act_lease.hpp"
#include "../action_constants.hpp"
#include "../transaction.hpp"
#include <iostream>

namespace etcdv3 {

//-----------------------------------------------------------------------------
AsyncLeaseGrantAction::AsyncLeaseGrantAction(const ActionParameters& param)
    : Action(param) {

    etcdserverpb::LeaseGrantRequest request;
    request.set_ttl(parameters.ttl);
    if (param.lease_id > 0) {
        request.set_id(param.lease_id);
    }

    context.set_deadline(parameters.gainDeadline());
    reader.reset(parameters.lease_stub->AsyncLeaseGrant(&context, request, &cq_).release());

    reader->Finish(&reply, &status, (void*)this);
}

AsyncLeaseGrantResponse AsyncLeaseGrantAction::ParseResponse() {
    AsyncLeaseGrantResponse lease_resp;
    if (!status.ok()) {
        lease_resp.set_error_code(status.error_code());
        lease_resp.set_error_message(status.error_message());
    } else {
        lease_resp.ParseResponse(reply);
    }
    return lease_resp;
}

//-----------------------------------------------------------------------------
AsyncLeaseRevokeAction::AsyncLeaseRevokeAction(const ActionParameters& param)
    : Action(param) {

    etcdserverpb::LeaseRevokeRequest request;
    request.set_id(param.lease_id);

    context.set_deadline(parameters.gainDeadline());
    reader.reset(parameters.lease_stub->AsyncLeaseRevoke(&context, request, &cq_).release());

    reader->Finish(&reply, &status, (void*)this);
}

AsyncLeaseRevokeResponse AsyncLeaseRevokeAction::ParseResponse() {
    AsyncLeaseRevokeResponse lease_resp;
    if (!status.ok()) {
        lease_resp.set_error_code(status.error_code());
        lease_resp.set_error_message(status.error_message());
    } else {
        lease_resp.ParseResponse(reply);
    }
    return lease_resp;
}

//-----------------------------------------------------------------------------
// init 1, write 2, read 3, done 4
AsyncLeaseKeepAliveAction::AsyncLeaseKeepAliveAction(const ActionParameters& param)
    : Action(param)
    , isCancelled(false) {

    request.set_id(param.lease_id);

    context.set_initial_metadata_corked(true);
    reader.reset(parameters.lease_stub->AsyncLeaseKeepAlive(&context, &cq_, (void*)1).release());

    reader->Write(request, opts, (void*)2);
    reader->Read(&reply, (void*)3);
}

AsyncLeaseKeepAliveResponse AsyncLeaseKeepAliveAction::ParseResponse() {
    AsyncLeaseKeepAliveResponse lease_resp;
    if (!status.ok()) {
        lease_resp.set_error_code(status.error_code());
        lease_resp.set_error_message(status.error_message());
    } else {
        lease_resp.ParseResponse(reply);
    }
    return lease_resp;
}

void AsyncLeaseKeepAliveAction::waitForResponse(std::function<void(const etcd::Response&)> callback) {
    void* got_tag;
    bool ok = false;

    while (cq_.Next(&got_tag, &ok)) {
        if (ok == false) {
            break;
        }
        if (got_tag == (void*)4) {
            isCancelled = true;
        } else if (got_tag == (void*)3) { // read tag
            if (reply.id() > 0) {
                callback(ParseResponse());
            }
            reader->Write(request, opts, (void*)2);
            reader->Read(&reply, (void*)3);
        }
    }
}

void AsyncLeaseKeepAliveAction::Cancel() {
    if (isCancelled == false) {
        reader->WritesDone((void*)4);
    }
}

//-----------------------------------------------------------------------------
AsyncLeaseTimeToLiveAction::AsyncLeaseTimeToLiveAction(const ActionParameters& param, bool keys)
    : Action(param) {

    etcdserverpb::LeaseTimeToLiveRequest request;
    request.set_id(param.lease_id);
    request.set_keys(keys);

    reader.reset(parameters.lease_stub->AsyncLeaseTimeToLive(&context, request, &cq_).release());
    reader->Finish(&reply, &status, (void*)this);
}

AsyncLeaseTimeToLiveResponse AsyncLeaseTimeToLiveAction::ParseResponse() {
    AsyncLeaseTimeToLiveResponse lease_resp;
    if (!status.ok()) {
        lease_resp.set_error_code(status.error_code());
        lease_resp.set_error_message(status.error_message());
    } else {
        lease_resp.ParseResponse(reply);
    }
    return lease_resp;
}

}
